package sfsoftware.game.actor.item;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

import sfsoftware.game.actor.attributes.Collidable;
import sfsoftware.game.actor.attributes.Obtainable;
import sfsoftware.game.trevor.Player;
import sfsoftware.game.trevor.ui.Dialog;
import sfsoftware.util.Actor;
import sfsoftware.util.Sound;
import sfsoftware.util.Updatable;
import sfsoftware.util.graphics.Graphics;
import sfsoftware.util.graphics.Particle;

public class Item extends Actor implements Obtainable, Updatable {
	public static final int PASSIVE = 0;
	public static final int USEABLE = 1;
		
	private static final String[] ONE_TIME_USE_ITEMS;
	private static final String[] PASSIVE_ITEMS;
	private static final String[] UNIQUE_ITEMS;
	
	public static Properties properties = new Properties();	
	
	private static final String LUCK_MODIFIER_PROPERTY = ".modifier.luck";
	private static final String SPIRIT_MODIFIER_PROPERTY = ".modifier.spirit";
	private static final String DAMAGE_RESISTANCE_PROPERTY = ".modifier.damageresistance";
	private static final String WIFFA_DAMAGE_PROPERTY = ".modifier.wiffadamage";
	
	private float damageResistanceModifier = 0;
	private float luckModifier = 0;
	private float spiritModifier = 0;
	private float wiffaDamageModifier = 0;
	
	// Default item type is a useable one.
	private int type = USEABLE;	
	private String name;
	private boolean pickedUp = false;
	
	private static Vector<Sparkle> sparkles = new Vector<Sparkle>();
	
	static 
	{		
		try 
		{
            // Load the item properties file
			properties.load(new FileInputStream("res/item.properties"));			
		} 
		catch (IOException ex) 
		{			
			ex.printStackTrace();
		}	
		
		ONE_TIME_USE_ITEMS = properties.getProperty("items.oneTimeUse").split("\\s*,\\s*");
		PASSIVE_ITEMS = properties.getProperty("items.passive").split("\\s*,\\s*");
		UNIQUE_ITEMS = properties.getProperty("items.unique").split("\\s*,\\s*");
	}
	
	public Item(int destX, int destY, String texture, int destWidth,
			int destHeight) {
		super(destX, destY, texture, destWidth, destHeight);
		name = texture; 

		// TODO Set modifiers for statistics here.
		luckModifier = getItemPropertyAsFloat(LUCK_MODIFIER_PROPERTY);
		spiritModifier = getItemPropertyAsFloat(SPIRIT_MODIFIER_PROPERTY);
		damageResistanceModifier = getItemPropertyAsFloat(DAMAGE_RESISTANCE_PROPERTY);
		wiffaDamageModifier = getItemPropertyAsFloat(WIFFA_DAMAGE_PROPERTY);
		
		// If the item that was created is a non-useable one, set the type to PASSIVE
		if(Arrays.asList(PASSIVE_ITEMS).contains(name.toLowerCase()))
		{
			type = PASSIVE;
		}
		// If this is a unique item and they already have it, remove it from existence!
		if(isUnique() && Player.getInventory().contains(name.toLowerCase()))
		{
			remove();
		}
		
	}
	
	public Item(int destX, int destY, String texture, int srcX, int srcY,
			int destWidth, int destHeight, String itemName) {
		super(destX, destY, texture, srcX, srcY, destWidth, destHeight);
		name = itemName;
		
		// TODO Set modifiers for statistics here.
		luckModifier = getItemPropertyAsFloat(LUCK_MODIFIER_PROPERTY);
		spiritModifier = getItemPropertyAsFloat(SPIRIT_MODIFIER_PROPERTY);
		damageResistanceModifier = getItemPropertyAsFloat(DAMAGE_RESISTANCE_PROPERTY);
		wiffaDamageModifier = getItemPropertyAsFloat(WIFFA_DAMAGE_PROPERTY);
		
		// If the item that was created is a non-useable one, set the type to PASSIVE
		if(Arrays.asList(PASSIVE_ITEMS).contains(name.toLowerCase()))
		{
			type = PASSIVE;
		}
		// If this is a unique item and they already have it, remove it from existence!
		if(isUnique() && Player.getInventory().contains(name.toLowerCase()))
		{
			remove();
		}
				
	}

	@Override
	public void collisionResult(Collidable object) 	
	{ 
		if(!(pickedUp)) 
		{
			pickedUp = true;
			
			if(isUnique())
			{
				Sound.playSound("unique_item");
				if(!(Dialog.displayDialog))
				{
					// Inform the player what item they've picked up!
					Dialog.raiseDialog("You got the " + getItemName().toUpperCase() + "!");
				}
			}
			else
			{
				Sound.playSound("item");
			}
			Player.addToInventory(this);			
		}
	}
	
	@Override
	public boolean equals(Object obj){
		boolean result = false;
				
		if(obj instanceof Item)
		{
			if(((Item) obj).toString().equalsIgnoreCase(this.toString()))
			{				
				result = true;
			}
		}
		else if(obj instanceof String)
		{
			String s = (String) obj;
			if(s.equalsIgnoreCase(this.toString()))
			{							
				result = true;
			}
		}
		
		return result;
	}
	
	private float getItemPropertyAsFloat(String propertyName)
	{
		float f = 0;		
		String value = getItemProperty(propertyName);
		
		if(value != null)
		{
			f = Float.valueOf(value);
		}
		
		return f;
	}
	
	private String getItemProperty(String propertyName)
	{		
		// If the supplied property name doesn't begin with a period, add one to the beginning!
		if(!(propertyName.startsWith(".")))
		{
			propertyName = "." + propertyName;
		}
		// Prefix the property name with the item name
		propertyName = getItemName().toLowerCase().replaceAll(" ", "_") + propertyName;
		
		return properties.getProperty(propertyName, null);
	}
	
	/**
	 * Retrives an item's name
	 * 
	 * @return String containing the item name
	 */
	public String getItemName()
	{
		return name;
	}
	
	public int getItemType()
	{
		return type;
	}
	
	/**
	 * Determines if the item is a one-time use item
	 * 
	 * @return boolean containing true if item is one-time use, otherwise false
	 */	
	public boolean isOneTimeUse()
	{
		if(Arrays.asList(ONE_TIME_USE_ITEMS).contains(name.toLowerCase()))
		{
			return true;
		}
		
		return false;
	}
	
	/**
	 * Determines if the item is a unique, one-of-a-kind item
	 * 
	 * @return boolean containing true if item is unique, otherwise false
	 */
	public boolean isUnique()
	{
		if(Arrays.asList(UNIQUE_ITEMS).contains(name.toLowerCase()))
		{
			return true;
		}
		
		return false;
	}
	
	@Override
	public void draw()
	{	
		if(pickedUp)
		{
			//TODO - animation for unique items?
			this.remove();
		}
		else
		{
			super.draw();
			
			if(isUnique())
			{
				if(((int) (Math.random() * 20)) <= 0)
				{
					new Sparkle();
				}				
				Sparkle[] s = new Sparkle[sparkles.size()];
				sparkles.toArray(s);			
				for(int i = 0; i < s.length; i++)		
				{
					s[i].draw();
				}
			}
		}
	}	
	
	/**
	 * Returns an ArrayList containing all item names found in the item.properties file
	 * 
	 * @return	ArrayList containing all item names
	 */
	public static ArrayList<String> getItemNames()
	{
		ArrayList<String> al = new ArrayList<String>();
		
		// Retrieve all of the property names from the property file and loop through each
		Enumeration<?> e = properties.propertyNames();		
		while (e.hasMoreElements()) 
		{
		      String key = (String) e.nextElement();
		      String itemName = key.substring(0, key.indexOf("."));
		      
		      // Replace underscores with spaces and format the String
		      itemName = itemName.replaceAll("_", " ");
		      itemName = sfsoftware.util.StringUtils.capitalizeString(itemName);
		      
		      // Only add to the list if the name isn't the generic Items property and the list doesn't already contain
		      if((!itemName.equals("Items")) && !(al.contains(itemName)))
		      {
		    	  
		    	  al.add(itemName);
		      }
		}
		// Sort the item name alphabetically
		Collections.sort(al);		
		return al;
	}
	
	public float getDamageResistanceModifier()
	{
		return damageResistanceModifier;
	}
	
	public float getLuckModifier()
	{
		return luckModifier;
	}
	
	public float getSpiritModifier()
	{
		return spiritModifier;
	}	
	
	public float getWiffaDamageModifier()
	{
		return wiffaDamageModifier;
	}
	
	public static boolean isUnique(String itemName)
	{
		if(Arrays.asList(UNIQUE_ITEMS).contains(itemName.toLowerCase()))
		{
			return true;
		}
		
		return false;		
	}
	
	public boolean isUseable()
	{
		return (type == USEABLE);
	}
	
	/**
	 * Creates a random drop item at the specified x and y coordinates.
	 * 
	 * @param x
	 * @param y
	 */
	public static void randomDropItem(int x, int y)
	{
		int randomItem = (int) (Math.random() * 2);			
		switch(randomItem)		
		{
			case 0:			
				new Ruple(x, y, "ruple", ((int)(Math.random() * 4)) * 16, 0, 16, 16);
				break;			
			case 1:
				new Heart(x, y, "heart", 16, 16);					
				break;
		}		
	}
	
	/**
	 * Creates a rarer random drop item at the specified x and y coordinates.
	 * 
	 * @param x
	 * @param y
	 */
	public static void randomRareDropItem(int x, int y)
	{
		int randomItem = (int) (Math.random() * 1);
		switch(randomItem)		
		{
			case 0:
				// Bomb...
				new Item(x, y, "items", 80, 0, 40, 40, "Bomb");
				break;
		}		
	}
	
	@Override
	public String toString()
	{
		return getItemName();
	}
	
	public void use(Actor user)
	{
		if(isUseable())
		{
			int x = (int) user.getX();
			int y = (int) user.getY();
			int width = (int) user.getWidth();
			int height = (int) user.getHeight();
			
			switch(this.toString().toLowerCase())
			{
				case "bomb":
					new Bomb(x, y);
					break;
				case "snow dust":
					Particle.generateParticles(x, y, width, height, 500, 1, 1, 1, 1);
					Sound.playSound("snowdust");
					Actor.freezeCast();			
					break;
				case "toxic powder":
					Sound.playSound("poison");
					Actor.poisonCast();
					break;
			}
		}
		else
		{
			//TODO Throw an exception or something...
		}
	}

	@Override
	public void update() {
		//TODO Handle item cool-down periods, etc. here

	}
	
	private class Sparkle
	{
		private int x;
		private int y;
		private sfsoftware.util.Clock c;
		private int sparkleState = 0;
		public Sparkle() 
		{
			x = (int) (getX() + (int) (Math.random() * getWidth()));
			y = (int) (getY() + (int) (Math.random() * getHeight()));
			c = new sfsoftware.util.Clock(50);
			sparkles.add(this);
		}		
		public void draw()
		{
			switch(sparkleState)
			{
				case 0:
					Graphics.drawQuad(x, y, 1, 1, 1, 1, 1, 1);
					break;
				case 1:
					Graphics.drawQuad(x - 1, y, 3, 1, 1, 1, 1, 1);
					Graphics.drawQuad(x, y - 1, 1, 3, 1, 1, 1, 1);					
					break;
				case 2:
					Graphics.drawQuad(x - 2, y, 5, 1, 1, 1, 1, 1);
					Graphics.drawQuad(x, y - 2, 1, 5, 1, 1, 1, 1);	
					break;
				case 3:
					Graphics.drawQuad(x - 1, y, 3, 1, 1, 1, 1, 1);
					Graphics.drawQuad(x, y - 1, 1, 3, 1, 1, 1, 1);					
					break;
				case 4:
					Graphics.drawQuad(x, y, 1, 1, 1, 1, 1, 1);
					break;					
				default:
					sparkles.remove(this);
			}
			if(c.updateClock())
			{
				sparkleState++;
			}
		}		
	}	
}